<?php
	include_once('Process.php');
	
class Scheduler{
	
	public function verfahren ($processHandler, $name, $quantum){
		/*zuruecksetzen des Schedulers fur ein neues Verfahren*/
		$max_runtime =  $processHandler->getRuningTimeKom() + $processHandler->getArriveTimeHigh () +1;
		$time = 0;
		$new = false;
		$currentProcesses = array();
		$array = array();
		$pointer = 0;
		
		
		while($time < $max_runtime){
			/*Sind neue Processe angekommen?*/
			foreach($processHandler->getProcessAtArriveTime($time) as $newProcess){
				array_push($currentProcesses, $newProcess);
				$new = true;
			}
			/*-----Scheduling Schritt-------------------*/
			/*Verfahrens auswahl*/
			if(count($currentProcesses) > 0){
				if ($name == 'FCFS'){
					$process = $this->get_process_with_shortest_arrive_time($currentProcesses);
				}else if($name == 'LCFS'){
					$process = $this->get_process_with_longest_arrive_time($currentProcesses);
				}else if($name == 'SJF'){
					$process = $this->get_process_with_shortest_runing_time($currentProcesses);
				}else if($name == 'LJF'){
					$process = $this->get_process_with_longest_runing_time($currentProcesses);
				}else if($name == 'LRTF'){
					$process = $this->get_process_with_longest_remaining_time($currentProcesses);
				}else if($name == 'SRTF'){
					$process = $this->get_process_with_shortest_remaining_time($currentProcesses);
				}else if($name == 'PS'){
					$process = $this->get_process_with_highest_priority($currentProcesses);
				}
				
				if (($new && ($this->getIndex($process, $array) == -1) )|| ($array[count($array)-1]->getRemainingRuntime() == 0 && ($this->getIndex($process, $array) == -1))){
					/*Neuer Process wird integriert && wenn es einen alten gab wird der mit einem End Zeitpunkt versehen*/
					if (count($array)> 0){
						$array [count($array)- 1]->setEndTime($time);
					}
					/*Process Starten*/
					$process = $this->startProcess($process, $time);
					array_push($array ,$process);
				} else if(count($array) > 0 && $array [count($array)- 1]->getRemainingRuntime ()> 0 && ($array [count($array)- 1] == $process || !$new)){
					/*Process wird weiter ausgeführt*/
						$array [count($array)- 1]->setRemainingRuntime ($array [count($array)- 1]->getRemainingRuntime ()-1);
				} else {
					/*Schonmal gelaufender Process wird erneut gestartet && der alten wird mit einem End Zeitpunkt versehen*/
					if (count($array)> 0){
						$array [count($array)- 1]->setEndTime($time);
					}
					/*Process mit den Aktuellen Daten holen*/
					$process = $array[$this->getIndex($process, $array)];
					/*Alter Process löschen*/
					$currentProcesses = $this->deleteProcess($currentProcesses, $process);
					//Clone und Inizialesiere den Process
					$procesClone = $this->cloneProcess($process, $time);
					/*Neuen Process einbinden in den ablauf*/
					array_push($currentProcesses ,$procesClone);
					array_push($array ,$procesClone);
				}
				/*Processe die eine Laufzeit == 0 haben sicher loeschen*/
				foreach($currentProcesses as $value){		
					if ($value->getRemainingRuntime () ==  0){
						$array[$this->getIndex($value, $array)]->setEndTime($time+1);
						$currentProcesses = $this->deleteProcess($currentProcesses, $value);
					}
				}
			}
			/*-----Scheduling Schritt Ende -----*/
			/*neue Runde inizialesieren*/
			$time++;
			$new = false;
		}
		/*setze das ergebniss des Verfahrens und gibt es zurueck*/
		return $this->verfahrenEnde($name, $array, $processHandler);
	}
	
	public function RoundRobing ($processHandler,$quantum){
		/*zuruecksetzen des Schedulers fur ein neues Verfahren*/
		$max_runtime =  $processHandler->getRuningTimeKom() + $processHandler->getArriveTimeHigh () +1;
		$time = 0;
		$new = false;
		$currentProcesses = array();
		$array = array();
		$pointer = 0;
		$counterTime = $quantum;
				
		while($time < $max_runtime){
			/*Sind neue Processe angekommen?*/
			foreach($processHandler->getProcessAtArriveTime($time) as $newProcess){
				array_push($currentProcesses, $newProcess);
				$new = true;
			}
			/*-----Scheduling Schritt-------------------*/
			/*Verfahrens auswahl*/
			if(count($currentProcesses) > 0){
				if ($counterTime == $quantum ){
					if ($pointer >= count($currentProcesses) || $pointer < 0){
						$pointer = 0;
					}
					$process = $currentProcesses[$pointer];
					$pointer++;
					$new = true;
					$counterTime = 0;
				}			
				
				if (($new && ($this->getIndex($process, $array) == -1) )|| ($array[count($array)-1]->getRemainingRuntime() == 0 && ($this->getIndex($process, $array) == -1))){
					/*Neuer Process wird integriert && wenn es einen alten gab wird der mit einem End Zeitpunkt versehen*/
					if (count($array)> 0){
						$array [count($array)- 1]->setEndTime($time);
					}
					/*Process Starten*/
					$process = $this->startProcess($process, $time);
					array_push($array ,$process);
				} else if($quantum > 1 && $time - $array [count($array)- 1]->getStartTime() < $quantum){
					/*Process wird weiter ausgeführt*/
						$array [count($array)- 1]->setRemainingRuntime ($array [count($array)- 1]->getRemainingRuntime ()-1);
				} else {
					/*Schonmal gelaufender Process wird erneut gestartet && der alten wird mit einem End Zeitpunkt versehen*/
					if (count($array)> 0){
						$array [count($array)- 1]->setEndTime($time-1);
					}
					/*Process mit den Aktuellen Daten holen*/
					$processIndex = $this->getIndex($process, $array);
					$process = $array[$processIndex];
					//Clone und Inizialesiere den Process
					$procesClone = $this->cloneProcess($process, $time);
					/*Neuen Process einbinden in den ablauf*/
					unset($currentProcesses[$pointer-1]);
					$currentProcesses[$pointer-1] = $procesClone;
					array_push($array ,$procesClone);
				}
				/*Processe die eine Laufzeit == 0 haben sicher loeschen*/
				foreach($currentProcesses as $value){		
					if ($value->getRemainingRuntime () ==  0){
						$array[$this->getIndex($value, $array)]->setEndTime($time+1);
						$currentProcesses = $this->deleteProcess($currentProcesses, $value);
						$pointer--;
					}
				}
				$counterTime++;
			}
			/*-----Scheduling Schritt Ende -----*/
			/*neue Runde inizialesieren*/
			$time++;
			$new = false;
		}
		/*setze das ergebniss des Verfahrens und gibt es zurueck*/
		return $this->verfahrenEnde('RR', $array, $processHandler);
	}
	
	//Gibt den Process mit der Laengsten CPU-Restlaufzeit zurueck
	private function get_process_with_longest_remaining_time($processes) {		
		$currentMax = 0;
		$result;
		foreach ($processes as $value) {
			if($value->getRemainingRuntime () > $currentMax && $value->getRemainingRuntime () != 0) {
				$result = $value;
				$currentMax = $value->getRemainingRuntime ();
			} else if ($value->getRemainingRuntime () == 0){
				$result = $value;
			}
		}
		return $result;
	}
	
	//Gibt den Process mit der hoechsten Prioritaet zurueck
	private function get_process_with_highest_priority($processes) {		
		$currentMax = 0;
		foreach ($processes as $value) {
			if($value->getPriority () > $currentMax) {
				$result = $value;
				$currentMax = $value->getPriority ();
			}
		}
		return $result;
	}
	
	//Gibt den Process mit der Kuerzesten CPU-Restlaufzeit zurueck
	private function get_process_with_shortest_remaining_time($processes) {		
		$currentMin = 501;
		foreach ($processes as $value) {
			if($value->getRemainingRuntime () < $currentMin) {
				$result = $value;
				$currentMin = $value->getRemainingRuntime ();
			}
		}
		return $result;
	}
	
	//Gibt den Process mit der Kuerzesten Ankunftszeit zurueck
	private function get_process_with_shortest_arrive_time($processes) {		
		$currentMin = 101;
		foreach ($processes as $value) {
			if($value->getArrive () < $currentMin) {
				$result = $value;
				$currentMin = $value->getArrive ();
			}
		}
		return $result;
	}
	
	//Gibt den Process mit der Laengsten Ankunftszeit zurueck
	private function get_process_with_longest_arrive_time($processes) {		
		$currentMax = -1;
		foreach ($processes as $value) {
			if($value->getArrive () > $currentMax) {
				$result = $value;
				$currentMax = $value->getArrive ();
			}
		}
		return $result;
	}
	
	//Gibt den Process mit der Kuerzesten CPU-Laufzeit zurueck
	private function get_process_with_shortest_runing_time($processes) {		
		$currentMin = 501;
		foreach ($processes as $value) {
			if($value->getRuntime () < $currentMin) {
				$result = $value;
				$currentMin = $value->getRuntime ();
			}
		}
		return $result;
	}
	
	//Gibt den Process mit der Laengsten CPU-Laufzeit zurueck
	private function get_process_with_longest_runing_time($processes) {		
		$currentMax = 0;
		foreach ($processes as $value) {
			if($value->getRuntime () > $currentMax) {
				$result = $value;
				$currentMax = $value->getRuntime ();
			}
		}
		return $result;
	}
	
	/*Sucht den Index des übergebenen Objektes in dem Array*/
	private function getIndex ($process, $array){
		if (count($array)){
			for($counter = 0; $counter < count($array);$counter++){
				if ($array[$counter] == $process){
					return $counter;
				}
			}
		}
		return -1;
	}
	
	/*Inizialesiert einen Process der die CPU zugewisen bekommt*/
	private function startProcess ($process, $time){
		$process->setStartTime($time);
		$process->setRemainingRuntime ($process->getRemainingRuntime ()-1);
		return $process;	
	}
	
	/*Clont und inizialesiert einen Process*/
	private function cloneProcess ($process, $time){
		$procesClone = clone $process;
		$procesClone->setStartTime($time);
		$procesClone->setRemainingRuntime ($process->getRemainingRuntime ()-1);
		return $procesClone;
	}
	
	/*Loescht einen Process sicher aus dem Array*/
	private function deleteProcess ($array, $process){
		unset($array[$this->getIndex($process, $array)]);
	  	return array_values($array);
	}
	
	/*Setzt den Namen und das Array wieder in dem Handler*/
	private function verfahrenEnde ($name, $array, $processHandler){
		$processHandler->setVerfahren($name);
		$processHandler->setProcessArray($array);
		return $processHandler;
	}
}
?>